grammar Program;

@header {
package antlr;

import testaux.*;
import java.util.Arrays;
}

program returns [Program value]
    :   i=ident SEMICOLON fc=func_list {$value = new Program($i.value, $fc.value);}
    ;

func_list returns [FunctionList value]
    :   f=func_decl {$value = new FunctionList($f.value);}
        (   fc=func_list {$value = new FunctionList($f.value, $fc.value);}    )?
    ;

func_decl returns [Function value]
    :   FUNCTION i=ident LPAR
        (   ac=ident_list RPAR b=block {$value = new Function($i.value, $ac.value, $b.value);}
        |   RPAR b=block {$value = new Function($i.value, null, $b.value);}
        )
    ;

block returns [Block value]
    :   LC sc=st_list RC {$value = new Block($sc.value);}
    ;

ident_list returns [StringList value]
    :   i=ident {$value = new StringList($i.value);}
        (   COMMA ic=ident_list {$value = new StringList($i.value, $ic.value);}   )?
    ;

st_list returns [StatementList value]
    :   st=statement
        (   SEMICOLON sc=st_list {$value = new StatementList($st.value, $sc.value);}
        |   {$value = new StatementList($st.value);}
        )
    ;

statement returns [Statement value]
    :   a=assignment {$value = new Statement(Sym.ASSIGN, $a.value);}
    |   b=block      {$value = new Statement(Sym.BLOCK, $b.value);}
    |   i=if_st      {$value = new Statement(Sym.IF, $i.value);}
    |   r=ret        {$value = new Statement(Sym.RET, $r.value);}
    |   PASS         {$value = new Statement(Sym.PASS, null);}
    ;


assignment returns [Assignment value]
    :   i=ident ASSIGN e=expr {$value = new Assignment($i.value, $e.value);}
    ;

if_st returns [IfStatement value]
    :   IF c=condition b1=block b2=block {$value = new IfStatement($c.value, $b1.value, $b2.value);}
    ;

condition returns [Condition value]
    :   LSQ e1=expr
        (   LT e2=expr RSQ {$value = new Condition(Sym.LT, $e1.value, $e2.value);}
        |   GT e2=expr RSQ {$value = new Condition(Sym.GT, $e1.value, $e2.value);}
        )
    ;

ret returns [Return value]
    :   RET e=expr {$value = new Return($e.value);}
    ;

expr returns [Expr value]
    :   e=term {$value = $e.value;}
        (   '+' e=term {$value = new Expr(Sym.ADD, null, Arrays.asList($value, $e.value));}
        |   '-' e=term {$value = new Expr(Sym.MIN, null, Arrays.asList($value, $e.value));}
        )*
    ;

term returns [Expr value]
    :   e=factor {$value = $e.value;}
        ('*' e=factor {$value = new Expr(Sym.MUL, null, Arrays.asList($value, $e.value));})*
    ;

factor returns [Expr value]
    :   na=nat {$value = new Expr(Sym.INTCONST, new Integer($na.value));}
    |   id=ident {$value = new Expr(Sym.IDENT, $id.value);}
    |   in=invocation {$value = new Expr(Sym.INVOCATION, $in.value);}
    |   '(' e=expr ')' {$value = $e.value;}
    ;

invocation returns [Invocation value]
    :   i=ident LPAR
        (   ec=expr_list RPAR {$value = new Invocation($i.value, $ec.value); }
        |   RPAR {$value = new Invocation($i.value, null);}
        )
    ;

expr_list returns [ExprList value]
    :   e=expr
        (   COMMA ec=expr_list {$value = new ExprList($e.value, $ec.value);}
        |   {$value = new ExprList($e.value);}
        )
    ;

nat returns [Integer value]
    :   INT {$value = new Integer($INT.text);}
    ;

ident returns [String value]
    :   IDENT {$value = new String($IDENT.text);}
    ;


LPAR : '(' ;
RPAR : ')' ;
LSQ : '[' ;
RSQ : ']' ;
LC : '{' ;
RC : '}' ;
LT : '<' ;
GT : '>' ;
ASSIGN : '=' ;
SEMICOLON : ';' ;
COMMA : ',' ;
IF : 'IF' ;
FUNCTION : 'DEF' ;
PASS : 'PASS' ;
RET : 'RET' ;

IDENT
    : ('a'..'z' | 'A'..'Z') ('a'..'z' | '0'..'9')* ;

INT : '0'..'9'+ ;

WS  :   (' '|'\t'|'\r'|'\n')+ {skip();} ;